package org.syndicate.domain;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.OneToMany;
import javax.persistence.ManyToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
import org.springframework.transaction.annotation.Transactional;
import org.syndicate.util.CalendarUtil;

/**
 * Loan domain.<br/>
 * It draws out from Facility and the loan is united. 
 * 
 * @author akiraabe
 */
@RooJavaBean
@RooToString
@RooEntity(inheritanceType = "JOINED", finders = { "findLoansByName" })
public class Loan extends AbstractPosition {

    private Double principal;

    private Integer paymentTime;

    private Double rate;

    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "S-")
    private Date startDate;

    private String description;

    @ManyToMany(cascade = CascadeType.ALL)
    private List<PaymentDetail> paymentDetails = new ArrayList<PaymentDetail>();

    /**
	 * {@inheritDoc}
	 */
    @Override
    public void apply(AbstractTransaction transaction) {
        System.out.println("Loan#apply");
        if ("Drawdown".equals(transaction.getDiscriminator())) {
            Drawdown drawdown = (Drawdown) transaction;
            setPrincipal(drawdown.getAmount());
            setPaymentTime(drawdown.getPaymentTime());
            setRate(drawdown.getRate());
            setStartDate(drawdown.getTransactionDate());
            setName(drawdown.getName());
            setDescription("Loan domain made up by drawdown transaction.");
            createPaymentDetails();
            
            SharePie newSharePie;
            if ("AmountPie".equals(transaction.getSharePie().getDiscriminator())) {
                newSharePie = transaction.getSharePie();
            } else {
                newSharePie = transaction.getSharePie().prorate(getPrincipal());
                newSharePie.setName("Auto generated by drawdown.");
                newSharePie.persist();
            }
            setSharePie(newSharePie);
            setDiscriminator("Loan");
            transaction.setSharePie(newSharePie);
            persist();
            System.out.println(getSharePie());
            for (Share share : getSharePie().getShares()) {
                System.out.println("  " + share);
            }
        }
        
        if ("PrincipalPayment".equals(transaction.getDiscriminator())) {
        	AmountPie existingAmountPie = (AmountPie) this.getSharePie();
    		SharePie newSharePie = existingAmountPie.prorate(transaction.getAmount());
    		newSharePie.persist();
    		transaction.setSharePie(newSharePie);
     		existingAmountPie.minus(newSharePie);
    		persist();
        }
    }

    /**
	 * After the necessary field is set, 
	 * the payment details are calculated and made persisted. 
	 *
	 * @deprecated This method is for the unit test. Formally, please use "apply".
	 */
    @Transactional
    @Deprecated
    public void create() {
        createPaymentDetails();
        persist();
    }

    @Transactional
    void createPaymentDetails() {
        createPaymentDetails(calculatePayAmount());
    }

//    public static List<PaymentDetail> findPaymenDetailByDate(Long loanId, Date paymentDate) {
//        if (loanId == null) return null;
//        Loan loan = entityManager().find(Loan.class, loanId);
//        List<PaymentDetail> results = new ArrayList<PaymentDetail>();
//        for (PaymentDetail paymentDetail : loan.paymentDetails) {
//            if (paymentDate.equals(paymentDetail.getPaymentDate())) {
//                results.add(paymentDetail);
//            }
//        }
//        return results;
//    }

    private void createPaymentDetails(Long amount) {
        System.out.println("!!!!! createPaymentDetails !!!!!");
        Double balanceOfPrincipal = principal;
        for (int i = 0; i < paymentTime; i++) {
            PaymentDetail paymentDetail = new PaymentDetail();
            paymentDetail.setPaymentTime(i + 1);
            paymentDetail.setPaymentDate(CalendarUtil.addMonth(startDate, i));
            paymentDetail.setInterest((long) Math.floor(balanceOfPrincipal * rate / 1200));
            paymentDetail.setPrincipal(amount - paymentDetail.getInterest());
            paymentDetail.setAmount(amount);
            paymentDetail.setBalanceOfPrincipal((balanceOfPrincipal - paymentDetail.getPrincipal()));
            balanceOfPrincipal = paymentDetail.getBalanceOfPrincipal();
            addPaymentDetail(paymentDetail);
            System.out.println(paymentDetail);
        }
    }

    private Long calculatePayAmount() {
        Integer cycle = 1;
        Integer site = 1;
        Double monthlyRate = (rate / 1200) * cycle;
        Double paymentRate = 0.0;
        Long amount = 0L;
        paymentRate = monthlyRate * (1 + monthlyRate * site / cycle) * Math.pow((1 + monthlyRate), (paymentTime - 1)) / (Math.pow((1 + monthlyRate), paymentTime) - 1);
        amount = (long) Math.floor(principal * paymentRate);
        System.out.println("amount is " + amount + "!!!");
        return amount;
    }

    private void addPaymentDetail(PaymentDetail paymentDetail) {
        paymentDetails.add(paymentDetail);
    }
}
